package com.his.his_system.common.config;


import cn.hutool.json.JSONUtil;
import com.his.his_system.common.filter.JwtAuthenticationTokenFilter;
import com.his.his_system.common.filter.MenuAccessDecisionManager;
import com.his.his_system.common.filter.MenuFilterInvocationSecurityMetadataSource;
import com.his.his_system.common.handler.AuthenticationFail;
import com.his.his_system.common.handler.AuthenticationSuccess;
import com.his.his_system.service.impl.DzmHisMemberServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.time.Duration;
import java.util.Collections;
import java.util.HashMap;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    @Autowired
    AuthenticationSuccess successFilter;
    @Autowired
    AuthenticationFail failFilter;
    @Autowired
    DzmHisMemberServiceImpl loginService;

    @Autowired
    MenuFilterInvocationSecurityMetadataSource menuFilterInvocationSecurityMetadataSource;

    @Autowired
    MenuAccessDecisionManager menuAccessDecisionManager;


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //允许跨域请求
                .cors().and()
                //关闭csrf
                .csrf().disable()
                .formLogin()
                .loginProcessingUrl("/his/login")
//                登录成功
                .successHandler(successFilter)
//                登录失败
                .failureHandler(failFilter)
                .and()
//                退出登录
                .logout()
                .logoutUrl("/logout")
                .logoutSuccessHandler((req, resp, auth) -> {
                    resp.setContentType("application/json;charset=utf-8");
                    PrintWriter out = resp.getWriter();
                    HashMap<Object, Object> resutlMap = new HashMap<>();
                    resutlMap.put("code", 502);
                    resutlMap.put("msg", "退出成功");
                    out.write(JSONUtil.toJsonStr(resutlMap));
                    out.flush();
                    out.close();
                })
                .deleteCookies("JSESSIONID")
                .clearAuthentication(true)
                .and()
//                认证异常处理
                .exceptionHandling()
                .authenticationEntryPoint((req, resp, authException) -> {
                            System.out.println("认证异常");
                            authException.printStackTrace();
                            resp.setContentType("application/json;charset=utf-8");
                            PrintWriter out = resp.getWriter();
                            HashMap<Object, Object> resutlMap = new HashMap<>();
                            resutlMap.put("code", 501);
                            resutlMap.put("msg", "尚未登录，请先登录");
                            out.write(JSONUtil.toJsonStr(resutlMap));
                            out.flush();
                            out.close();
                        }
                )
//             权限不足自定义返回结果
                .accessDeniedHandler((HttpServletRequest req, HttpServletResponse resp, AccessDeniedException e) -> {
                            resp.setContentType("application/json;charset=utf-8");
                            PrintWriter out = resp.getWriter();
                            HashMap<Object, Object> resutlMap = new HashMap<>();
                            resutlMap.put("code", 403);
                            resutlMap.put("msg", "权限不足");
                            out.write(JSONUtil.toJsonStr(resutlMap));
                            out.flush();
                            out.close();
                        }
                )
                .and()
                //不通过Session获取SecurityContext
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                // 对于登录接口 允许匿名访问
                .antMatchers("/IncomeAndExpenses/**").anonymous()
                .antMatchers("/api/**").anonymous()
//                .antMatchers("/sys/**").hasAuthority("管理员")
//                .antMatchers("**").anonymous()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                        object.setSecurityMetadataSource(menuFilterInvocationSecurityMetadataSource); //动态获取url权限配置
                        object.setAccessDecisionManager(menuAccessDecisionManager); //权限判断
                        return object;
                    }
                })
        ;


        //把token校验过滤器添加到过滤器链中
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }



    /**
     * 跨域配置
     *
     * @return
     */
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowCredentials(true);
        configuration.setAllowedOriginPatterns(Collections.singletonList("*"));
        configuration.setAllowedMethods(Collections.singletonList("*"));
        configuration.setAllowedHeaders(Collections.singletonList("*"));
        configuration.setMaxAge(Duration.ofHours(1));
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }


    /**
     * 认证管理器
     *
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 认证管理器构建
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(loginService);
    }


    /**
     * 用于测试app
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("app/**");
        super.configure(web);
    }
}
